/*
 * Copyright (c) 2018-2024 北京华恒盛世科技有限公司
 * QUICKPOOL is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
*/

#include "limit.h"
#include "xdrbase.h"
#include "common.h"
#include "yparse.h"
#include "lib.h"

extern char *yybuff;

typedef union {
    int ival;
    struct array_index  idxType;
    struct array_index *idxPtr;
} YYSTYPE;

# define yyerror idxerror
# define yynerrs idxnerrs
int idxerrno = IDX_NOERR;

#ifndef YYMAXDEPTH
#define YYMAXDEPTH 150
#endif
#ifndef __YYSCLASS
# define __YYSCLASS static
#endif

#define YYERROR goto yyerrlab
#define YYFLAG  (-3000)
#define YYLAST 48
#define YYERRCODE 256

#ifndef __RUNTIME_YYMAXDEPTH
#define YYACCEPT	return 0
#define YYABORT		return(1)
#else
#define YYACCEPT	{free_stacks(); return 0;}
#define YYABORT		{free_stacks(); return(1);}
#endif

struct mallocList  *idxAllocHead = NULL;
#define YYALLOC(x) yyalloc(&idxAllocHead, (x))

__YYSCLASS YYSTYPE *yypv;			
__YYSCLASS int *yyps;			
__YYSCLASS int yystate;			
__YYSCLASS int yytmp;			
int yynerrs;			
__YYSCLASS int yyerrflag;			
int yychar;			
long yymaxdepth = YYMAXDEPTH;
YYSTYPE yylval;
__YYSCLASS YYSTYPE yyval;

#ifndef nl_msg
#define nl_msg(i,s) (s)
#endif

# ifndef __RUNTIME_YYMAXDEPTH
__YYSCLASS YYSTYPE yyv[ YYMAXDEPTH ];	
__YYSCLASS int yys[ YYMAXDEPTH ];		
# else
__YYSCLASS YYSTYPE *yyv;			
__YYSCLASS int *yys;			
#endif 

typedef int yytabelem;
__YYSCLASS yytabelem yyexca[]={
    -1, 1,
    0, -1,
    -2, 0,
};

__YYSCLASS yytabelem yyact[]={
    12,     3,    14,    10,    15,    13,     4,     8,    10,     1,
     5,     2,    11,     7,     6,     0,     0,    16,     0,     0,
     0,     0,    18,    19,    17,     0,     0,     0,     0,     0,
     0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
     0,     0,     0,     0,     0,     0,     0,     9
};

__YYSCLASS yytabelem yypact[]={
   -90, -3000,   -31,     2,     7,   -93,   -39,   -56,   -41,     7,
 -3000, -3000, -3000,     2,     7,     7, -3000, -3000, -3000, -3000
};

__YYSCLASS yytabelem yypgo[]={
     0,    14,    13,    10,    11,     7,     9
};

__YYSCLASS yytabelem yyr1[]={
     0,     6,     6,     4,     3,     3,     1,     1,     2,     2,
     2,     2,     5
};
__YYSCLASS yytabelem yyr2[]={
     0,     3,     7,     7,     3,     7,     3,     7,     3,     5,
     5,     7,     3
};

__YYSCLASS yytabelem yychk[]={
 -3000,    -6,    -4,    91,    37,    -3,    -1,    -2,    -5,    45,
     1,    -5,    93,    44,    58,    45,    -5,    -3,    -5,    -5
};

__YYSCLASS yytabelem yydef[]={
     0,    -2,     1,     0,     0,     0,     4,     6,     8,     0,
    12,     2,     3,     0,     0,    10,     9,     5,     7,    11
};

static void make_key(void);

static pthread_key_t key;
static pthread_once_t key_once = PTHREAD_ONCE_INIT;

/**
 * @brief       split the long job ID into job ID and array index.
 *
 * @param[in]   longjobid     #1: long job ID being split
 * @param[out]  jobid         #2: job array ID
 * @param[out]  index         #3: job array index
 *
 */
void
split_jobid(JOBID_T longjobid, int *jobid, int *index)
{
    *index = ARRAY_INDEX(longjobid);
    *jobid = ARRAY_JOBID(longjobid);

    return;
} // end function split_jobid

/**
 * @brief       combine job ID and array index into long job ID.
 *
 * @param[out]  longjobid    #1: long job ID being split
 * @param[in]   jobid        #2: job array ID
 * @param[in]   index        #3: job array index
 *
 */
void
combine_jobid(JOBID_T *longjobid, int jobid, int index)
{
    *longjobid = QP_JOBID(jobid, index);

    return;
} // end function combine_jobid

/**
 * @brief       free the job array idx list.
 *
 * @param[in|out]   jobarrayidx  #1: idx list being freed
 *
 */
void
free_jobarray_idx(struct array_index *jobarrayidx)
{
    while (NULL != jobarrayidx) {
        struct array_index *nextIdx;

        nextIdx = jobarrayidx->next_index;
        FREEUP(jobarrayidx);
        jobarrayidx = nextIdx;
    }

    return;
} // end function free_jobarray_idx

/**
 * @brief       check if job name is the same.
 *
 * @param[in]   patten        #1: job name, with or without []
 * @param[in]   name          #2: array limit
 *
 * @retval      TRUE          #1: same
 * @retval      FALSE         #2: different
 *
 * @note  aaa[1-3] and aaa[2-4] have the same job name.
 */
bool_t
issame_jobname(char *pattern, char *name)
{
    if (NULL == pattern || NULL == name) {
        return FALSE;
    }

    unsigned len;
    len = strlen(pattern);
    if ('*' == pattern[0] && '\0' != pattern[1]) {

        char *strInStar;
        if ('*' == pattern[len-1]) {
            strInStar = strndup(pattern+1, len-2);
        } else {
            strInStar = copy_string(pattern+1);
        }

        char *sp;
        sp = strstr(name, strInStar);
        FREEUP(strInStar);

        if (NULL == sp) {
            return FALSE;
        }

        return TRUE;
    }

    unsigned int i;
    for (i=0; i<len && '['!=pattern[i]; i++) {

        if ('*' == pattern[i]) {
            return TRUE;
        }

        if ('\0' == name[i] || '[' == name[i] || pattern[i] != name[i]) {
            return FALSE;
        }
    }

    if ('\0' == name[i] || '[' == name[i]) {
        return TRUE;
    }

    return FALSE;
} // end function issame_jobname

/**
 * @brief       parse job name to job array idx.
 *
 * @param[in]   jobname       #1: job name, with or without []
 * @param[out]  limit         #2: array limit
 * @param[in]   maxarray      #3: setting of maximum array index
 *
 * @retval      not NULL      #1: succeed
 * @retval      NULL          #2: failed, job name is not an array job or index is too large
 *
 * @note  job name for array job: test[1-20].
 *        job name without [] means not an array job.
 */
struct array_index *
parse_jobname(char *jobname, int *limit, int maxarray)
{
    quickpool_errno = 0;

    if (NULL == jobname || '\0' == jobname[0]) {
        return NULL;
    }

    char *index;
    index = strchr(jobname, '[');
    if (NULL == index) {
        return NULL;
    }

    *limit = INFINIT_INT;

    struct array_index *jobArrayIdx = NULL, *loopIdx;
    yybuff = index;
    if (parse_array_index(&jobArrayIdx, limit)) {

        free_jobarray_idx(jobArrayIdx);

        if (idxerrno == IDX_MEM) {
            log_message(ERR, NOCLASS, STR_FUNC_FAIL, __func__, "malloc");
            quickpool_errno = ERROR_MEMORY;
        } else {
            log_message(ERR, NOCLASS, "Bad job array index list <%s>.", jobname);
            quickpool_errno = ERROR_JOB_ARRAY_INDEX;
        }

        return NULL;
    }

    int arraySize = 0;
    for (loopIdx = jobArrayIdx; NULL != loopIdx; loopIdx = loopIdx->next_index) {

        if (INFINIT_INT == loopIdx->end) {
            loopIdx->end  = loopIdx->start + (maxarray-1)*loopIdx->step;
        }

        if (loopIdx->start > loopIdx->end) {

            log_message(ERR, NOCLASS, "Job Array index invalid range: [%d-%d].",
                        loopIdx->start, loopIdx->end);
            free_jobarray_idx(jobArrayIdx);
            quickpool_errno = ERROR_JOB_ARRAY_INDEX;
            return NULL;
        }

        if (1 > loopIdx->start || MAXARRAYINDEX < loopIdx->start) {

            log_message(ERR, NOCLASS, "Job Array index out of valid range: [%d]", loopIdx->start);
            free_jobarray_idx(jobArrayIdx);
            quickpool_errno = ERROR_JOB_ARRAY_INDEX;
            return NULL;
        }

        if (1 > loopIdx->end || MAXARRAYINDEX < loopIdx->end) {

            log_message(ERR, NOCLASS, "Job Array index out of valid range: [%d]", loopIdx->end);
            free_jobarray_idx(jobArrayIdx);
            quickpool_errno = ERROR_JOB_ARRAY_INDEX;
            return NULL;
        }

        if (1 > loopIdx->step || MAXARRAYINDEX < loopIdx->step) {

            log_message(ERR, NOCLASS, "Job Array index out of valid range: [%d]", loopIdx->step);
            free_jobarray_idx(jobArrayIdx);
            quickpool_errno = ERROR_JOB_ARRAY_INDEX;
            return NULL;
        }

        arraySize += (loopIdx->end - loopIdx->start)/loopIdx->step + 1;
    }

    if (arraySize > maxarray) {

        log_message(ERR, NOCLASS, "Array index out of valid range: [%d/%d]",
                    arraySize, maxarray);
        free_jobarray_idx(jobArrayIdx);
        quickpool_errno = ERROR_JOB_ARRAY_BIGINDEX;
        return NULL;
    }

    return(jobArrayIdx);
} // end function parse_jobname

/**
 * @brief       get job ID and job array idx from the input string.
 *
 * @param[in]   string        #1: transfer this string to job ID
 * @param[out]  outjobid      #2: the job ID got from the input string
 * @param[out]  jobarrayidx   #3: the job array idx got from the input string
 *
 * @retval      0             #1: succeed
 * @retval      -1            #2: failed, syntax error
 *
 * @note  job ID and job array examples: 123, 123[3], 134[1-29]
 */
int
get_jobid_indicies(char *string, JOBID_T *outjobid, struct array_index **jobarrayidx)
{
    *jobarrayidx = NULL;

    quickpool_errno = 0;
    if (NULL == string) {
        log_message(ERR, NOCLASS, "No jobid is specified.");
        quickpool_errno = ERROR_JOBID_STRING;
        return -1;
    }

    char *strJobId;
    strJobId = copy_string(string);

    JOBID_T jobId = 0;
    char *strJobArray;
    strJobArray = strchr(strJobId, '[');
    if (NULL == strJobArray) { // not a job array.

      if (!isdigit_string(strJobId)) { // specified job ID has non-digit character.
            log_message(ERR, NOCLASS, "Specified jobid <%s> has non-digit character.", string);
            quickpool_errno = ERROR_JOBID_STRING;
            FREEUP(strJobId);
            return -1;
        }

        jobId = atoll(strJobId);
        if (0 > jobId) { // specified negative job ID.
            log_message(ERR, NOCLASS, "Specified jobid <%s> must be larger than 0.", string);
            quickpool_errno = ERROR_JOBID_STRING;
            FREEUP(strJobId);
            return -1;
	}

        *outjobid = jobId;
        FREEUP(strJobId);
        return 0;
    }

    int jobIdLen;
    if ((jobIdLen = (strJobArray - strJobId)) >= 16) {
        quickpool_errno = ERROR_JOBID_STRING;
        log_message(ERR, NOCLASS, "Specified jobid <%s> is longer than 16.", string);
        return -1;
    }

    strJobId[jobIdLen] = '\0';
    if (!isdigit_string(strJobId)) {
        log_message(ERR, NOCLASS, "Specified jobid <%s> has non-digit character.", strJobId);
        quickpool_errno = ERROR_JOBID_STRING;
        return -1;
    }

    jobId = atoll(strJobId);
    if (0 >= jobId || MAXARRAYJOBID < jobId) {
        log_message(ERR, NOCLASS, "Specified jobid <%s> must between [%d-%d].",
                    strJobId, 1, MAXARRAYJOBID);
        quickpool_errno = ERROR_JOBID_STRING;
        return -1;
    }

    *outjobid = jobId;

    int maxArray;
    *jobarrayidx = parse_jobname(string, &maxArray, MAXARRAYINDEX);
    if (NULL == *jobarrayidx) {
        return -1;
    }

    return 0;
} // end function get_jobid_indicies

/**
 * @brief       get job IDs from the input string.
 *
 * @param[in]   string        #1: transfer this string to job ID
 * @param[out]  outjobids     #2: job IDs got from the input string
 *
 * @retval      0             #1: succeed
 * @retval      -1            #2: failed, syntax error
 *
 * @note  job ID and job array examples: 123, 123[3], 134[1-29]
 */
int
get_jobids_fromstring(char *string, JOBID_T **outjobids)
{
    struct array_index *jobArrayIdx=NULL;
    JOBID_T oneJobId;
    if (0 > get_jobid_indicies(string, &oneJobId, &jobArrayIdx)) {
        return -1;
    }

    int maxNumJobIds=MAX_JOB_IDS;
    JOBID_T *jobIds;
    jobIds = (JOBID_T *)calloc(maxNumJobIds, sizeof(JOBID_T));
    if (NULL == jobIds) {
        return -1;
    }

    int numJobIds=0;
    if (NULL == jobArrayIdx) {

        jobIds[0] = oneJobId;
        numJobIds = 1;
        *outjobids = jobIds;

        return(numJobIds);
    }

    // for array job, put every element into outjobids.
    struct array_index *loopIdx;
    for (loopIdx=jobArrayIdx; NULL!=loopIdx; loopIdx=loopIdx->next_index) {

        int i, j;
        for (j=loopIdx->start; j<=loopIdx->end; j+=loopIdx->step) {

            JOBID_T combineJobId;
            combineJobId = QP_JOBID(oneJobId, j);

            if (numJobIds >= maxNumJobIds) {

                maxNumJobIds += MAX_JOB_IDS;
                jobIds = (JOBID_T *)realloc(jobIds, maxNumJobIds*sizeof(JOBID_T));
                if (NULL == jobIds) {
                    perror("get_jobids_fromstring:realloc");
                    return -1;
                }
            }

            for (i=0; i<numJobIds; i++) {
                if (combineJobId == jobIds[i]) {
                    break;
                }
            }

            if (i == numJobIds) {
                jobIds[numJobIds++] = combineJobId;
            }
        }
    }

    free_jobarray_idx(jobArrayIdx);
    *outjobids = jobIds;

    return (numJobIds);
} // end function get_jobids_fromstring

char *
jobid_2string(JOBID_T jobid, char string[])
{
    if (0 == ARRAY_INDEX(jobid)) {
        snprintf(string, 32, "%d",  ARRAY_JOBID(jobid));
    } else {
        snprintf(string, 32, "%d[%d]",  ARRAY_JOBID(jobid), ARRAY_INDEX(jobid));
    }

    return string;
}

char *
jobid_2longstring(JOBID_T jobid, char string[])
{
    snprintf(string, 32, "%lld", jobid);
    return string;
}

int *
gf_quickpool_errno(void)
{
    int *ptr;
    pthread_once(&key_once, make_key);
    if ((ptr = (int *)pthread_getspecific(key)) == NULL) {
        ptr = (int *)calloc(1, sizeof(int));
        if (NULL != ptr) {
            pthread_setspecific(key, ptr);
        }
    }

    return ptr;
} // end function gf_quickpool_errno

/* ------------------------------------------------
 *
 *  below are static functions used in this file
 *
 * ------------------------------------------------ */

static void
make_key(void)
{
    pthread_key_create(&key, NULL);
    return;
} // end function make_key


int
parse_array_index(struct array_index **idx, int *maxlimit)
{
    register YYSTYPE *yypvt;	
	
# ifdef __RUNTIME_YYMAXDEPTH
    if (allocate_stacks()) YYABORT;
# endif

    yypv = &yyv[-1];
    yyps = &yys[-1];
    yystate = 0;
    yytmp = 0;
    yynerrs = 0;
    yyerrflag = 0;
    yychar = -1;
    goto yystack;

    {
        register YYSTYPE *yy_pv;	
        register int *yy_ps;		
        register int yy_state;		
        register int  yy_n;		

        yy_pv = yypv;
        yy_ps = yyps;
        yy_state = yystate;
        goto yy_newstate;

    yystack:
        yy_pv = yypv;
        yy_ps = yyps;
        yy_state = yystate;

    yy_stack:

    if (++yy_ps >= &yys[yymaxdepth]) {
# ifndef __RUNTIME_YYMAXDEPTH
        yyerror((nl_msg(30002,"yacc stack overflow")));
        YYABORT;
# else
        YYSTYPE * yyv_old = yyv;
        int * yys_old = yys;
        yymaxdepth += YYINCREMENT;
        yys = (int *) realloc(yys, yymaxdepth * sizeof(int));
        yyv = (YYSTYPE *) realloc(yyv, yymaxdepth * sizeof(YYSTYPE));
        if (yys==0 || yyv==0) {
            yyerror( (nl_msg(30002,"yacc stack overflow")) );
            YYABORT;
        }

        yy_ps = (yy_ps - yys_old) + yys;
        yyps = (yyps - yys_old) + yys;
        yy_pv = (yy_pv - yyv_old) + yyv;
        yypv = (yypv - yyv_old) + yyv;
# endif

    }

    *yy_ps = yy_state;
    *++yy_pv = yyval;

    yy_newstate:
    if ((yy_n = yypact[yy_state]) <= YYFLAG)
        goto yydefault;		

    if ((yychar < 0) && ((yychar = yylex()) < 0 ))
        yychar = 0;		

    if (((yy_n += yychar) < 0) || (yy_n >= YYLAST))
        goto yydefault;

    if (yychk[yy_n = yyact[yy_n]] == yychar) {
        yychar = -1;
        yyval = yylval;
        yy_state = yy_n;
        if (yyerrflag > 0)
            yyerrflag--;
        goto yy_stack;
    }

    yydefault:
    if ((yy_n = yydef[yy_state]) == -2) {

        if ((yychar < 0) && ((yychar = yylex()) < 0))
            yychar = 0;		

        {
            register int *yyxi = yyexca;

            while ((*yyxi != -1) ||	(yyxi[1] != yy_state)) {
                yyxi += 2;
            }
            while ((*(yyxi += 2) >= 0) && (*yyxi != yychar));
 
           if ((yy_n = yyxi[1]) < 0)
                YYACCEPT;
        }
    }

    if (yy_n == 0) {

        switch (yyerrflag) {
        case 0:		
            yyerror( (nl_msg(30003,"syntax error")) );
            yynerrs++;
            goto skip_init;

            yy_pv = yypv;
            yy_ps = yyps;
            yy_state = yystate;
            yynerrs++;
            skip_init:
        case 1:
        case 2:		
            yyerrflag = 3;

            while (yy_ps >= yys) {
                yy_n = yypact[ *yy_ps ] + YYERRCODE;

                if (yy_n >= 0 && yy_n < YYLAST && yychk[yyact[yy_n]] == YYERRCODE) {

                    yy_state = yyact[ yy_n ];
                    goto yy_stack;
                }

                yy_ps--;
                yy_pv--;
            }

            YYABORT;
        case 3:		
            if (yychar == 0)	
                YYABORT;
            yychar = -1;
            goto yy_newstate;
        }
    }
		
    yytmp = yy_n;			
    yypvt = yy_pv;			

    {
        register int yy_len = yyr2[ yy_n ];

        if (!(yy_len & 01)) {
            yy_len >>= 1;
            yyval = ( yy_pv -= yy_len )[1];	
            yy_state = yypgo[yy_n = yyr1[yy_n]] + *(yy_ps -= yy_len) + 1;
            if (yy_state >= YYLAST || yychk[yy_state = yyact[yy_state]] != -yy_n) {
                yy_state = yyact[yypgo[yy_n]];
            }
            goto yy_stack;
        }
        yy_len >>= 1;
        yyval = (yy_pv -= yy_len)[1];	
        yy_state = yypgo[ yy_n = yyr1[ yy_n ] ] + *(yy_ps -= yy_len) + 1;
        if (yy_state >= YYLAST || yychk[yy_state = yyact[ yy_state]] != -yy_n) {
            yy_state = yyact[ yypgo[ yy_n ] ];
        }
    }

    yystate = yy_state;
    yyps = yy_ps;
    yypv = yy_pv;
    }
	
    switch (yytmp) {
    case 1:
       *idx = yypvt[-0].idxPtr;                   
       yparseSucc(&idxAllocHead);
       break;
    case 2:
        *idx = yypvt[-2].idxPtr;
        *maxlimit = yypvt[-0].ival;
        yparseSucc(&idxAllocHead);
        break;
    case 3:
        yyval.idxPtr = yypvt[-1].idxPtr;
        break;
    case 4:
        if ((yyval.idxPtr = (struct array_index *)YYALLOC(sizeof(struct array_index))) == NULL) {
            idxerror("No Memory");
            idxerrno = IDX_MEM;
            YYABORT;
        }
        yyval.idxPtr->start = yypvt[-0].idxType.start;
        yyval.idxPtr->end = yypvt[-0].idxType.end;
        yyval.idxPtr->step = yypvt[-0].idxType.step;
        yyval.idxPtr->next_index = NULL;
        break;
    case 5:
        if ((yyval.idxPtr = (struct array_index *)YYALLOC(sizeof(struct array_index))) == NULL) {
            idxerror("No Memory");
            idxerrno = IDX_MEM;
            YYABORT;
        }
        yyval.idxPtr->start = yypvt[-2].idxType.start;
        yyval.idxPtr->end = yypvt[-2].idxType.end;
        yyval.idxPtr->step = yypvt[-2].idxType.step;
        yyval.idxPtr->next_index = yypvt[-0].idxPtr;
        break;
    case 6:
        yyval.idxType.start = yypvt[-0].idxType.start;
        yyval.idxType.end   = yypvt[-0].idxType.end;
        yyval.idxType.step  = 1;
        if (yyval.idxType.start < 1 || yyval.idxType.start > yyval.idxType.end
            || yyval.idxType.step <= 0) {
            idxerror("boundary error");
            idxerrno = IDX_BOUND;
            YYABORT;
        } 
        break;
    case 7:
        yyval.idxType.start = yypvt[-2].idxType.start;
        yyval.idxType.end   = yypvt[-2].idxType.end;
        yyval.idxType.step  = yypvt[-0].ival;
        if (yyval.idxType.start < 1 || yyval.idxType.start > yyval.idxType.end
            || yyval.idxType.step <= 0) {
            idxerror("boundary error"); 
            idxerrno = IDX_BOUND;
            YYABORT;
        } 
        break;
    case 8:
        yyval.idxType.start = yypvt[-0].ival;
        yyval.idxType.end  = yyval.idxType.start;
        yyval.idxType.step = 1;   
        break;
    case 9:
        yyval.idxType.start = 1;
        yyval.idxType.end  = yypvt[-0].ival;
        yyval.idxType.step = 1;
        break;
    case 10:
        yyval.idxType.start = yypvt[-1].ival; 
        yyval.idxType.end  = INFINIT_INT;
        yyval.idxType.step = 1;
        break;
    case 11:
        yyval.idxType.start = yypvt[-2].ival;
        yyval.idxType.end  = yypvt[-0].ival;
        yyval.idxType.step = 1;
        break;
    case 12:
        yyval.ival = atoi(token);
        break;
    }
    goto yystack;		
} // end function parse_array_index
